package lambdasinaction.chap5.my;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import lambdasinaction.chap4.MyDish;

/**
 * 映射 一个非常常见的数据处理套路就是从某些对象中选择信息 比如在 SQL 里，你可以从列表中选择一列 StreamAPI也通过 map 和 flatMap方法提供了类似的工具
 * 对流中每一个元素应用函数 流支持 map方法，它会接受一个函数作为参数。 这个函数会被应用到每个元素上，并将其映射成一个新的元素 （使用映射一次，是因为它和转换类似，但其中的细微差别在于它是“创建一个新版本”而不是去“修改”）
 *
 * @author Fish
 * @since 0.0.1 2018/7/25 12:08
 */
public class MyMapping {

  public static void main(String[] args) {
    /**
     * 例如，下面的代码把方法引用MyDish::getName传给了 map 方法，来提取流中菜肴的名称
     */
    List<String> dishNames = MyDish.menu.stream().map(MyDish::getName).collect(Collectors.toList());
    dishNames.forEach(System.out::println);

    /**
     * 给定一个单词列表，你想要返回另一个列表，显示每个单词中有几个字母
     * 你需要对列表中每个元素应用一个函数，这听起来正好该用 map方法去做
     * 应用的函数应该接受一个单词，并返回其长度。
     * 你可以像下面这样，给 map 传递一个方法引用String::length来解决这个问题
     */
    List<String> words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
    List<Integer> wordLengths = words.stream().map(String::length).collect(Collectors.toList());
    System.out.println(wordLengths);

    /**
     * 提取菜名的例子，找出每道菜的名称都多长
     */
    List<Integer> dishNameLengths = MyDish.menu.stream().map(MyDish::getName).map(String::length)
        .collect(Collectors.toList());
    System.out.println(dishNameLengths);

    /**
     * 流的扁平化 flatMap
     * 使用 flatMap方法的效果是，各个数组并不是分别映射成一个流，而是映射成流的内容
     * 所有使用 map(Arrays::stream)时生成的流都会被合并起来，即扁平化为一个流。
     * 总结：flatMap方法让你把流中的每一个值都换成另一个流，然后把所有的流连接起来成为一个流。
     */
    List<String> words2 = Arrays.asList("Hello", "World");
    List<String> uniqueCharacters = words2.stream().map(w -> w.split("")).flatMap(Arrays::stream)
        .distinct().collect(Collectors.toList());
    System.out.println("flatMap uniqueCharacters: " + uniqueCharacters);
//Stream<String[]>    List<String> uniqueCharacters = words2.stream().map(w -> w.split("")).distinct()

    /**
     * 给定一个数字列表，如何返回一个由每个数的平方构成的列表呢？
     * 你可以利用map方法的 Lambda，接受一个数字，并返回该数字平方的 Lambda来解决这个问题
     */
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    List<Integer> squares = numbers.stream().map(n -> n * n).collect(Collectors.toList());
    System.out.println("squares: " + squares);

    /**
     * 给定两个数字列表，如何返回所有的数对呢？
     * 你可以使用 map来迭代两个列表，并生成数对，你需要让生成的流扁平化
     */
    List<Integer> numbers1 = Arrays.asList(1, 2, 3);
    List<Integer> numbers2 = Arrays.asList(3, 4);
    List<int[]> pairs = numbers1.stream()
        .flatMap(i -> numbers2.stream().map(j -> new int[]{i, j})).collect(Collectors.toList());
    pairs.forEach(pair -> System.out.println("(" + pair[0] + "," + pair[1] +")"));
  }

}
